using Test
using Dates
using FLEXINVERT
using FLEXINVERT.ObservationsCore: Observations, ObservationRecord, add_observation!, empty_observations,
                                   get_station_data, get_date_data, get_stations, get_date_range,
                                   filter_by_date_range!, summary_stats, parse_datetime
using FLEXINVERT.ObservationsIO: read_observations, is_header_line, parse_observation_line

@testset "Observations Core" begin
    @testset "ObservationRecord construction" begin
        rec = ObservationRecord(
            rec="AAA", yyyymmdd=20200312, hhmmss=50000,
            jdate=2458921.208333, avetime=0.041667,
            conc=420.1, err=0.0451, num=1
        )
        @test rec.rec == "AAA"
        @test rec.yyyymmdd == 20200312
        @test rec.conc == 420.1
        @test rec.err == 0.0451
    end

    @testset "Observations structure" begin
        obs = empty_observations()
        @test isempty(obs)
        @test length(obs) == 0

        # Add a test observation
        rec = ObservationRecord(
            rec="AAA", yyyymmdd=20200312, hhmmss=50000,
            jdate=2458921.208333, avetime=0.041667,
            conc=420.1, err=0.0451, num=1
        )
        idx = add_observation!(obs, rec; station_idx=1, source_file="test.txt")

        @test !isempty(obs)
        @test length(obs) == 1
        @test idx == 1

        # Check data was added correctly
        @test obs.stations[1] == "AAA"
        @test obs.concentrations[1] == 420.1
        @test obs.measurement_errors[1] == 0.0451
        @test obs.station_indices[1] == 1
        @test obs.source_files[1] == "test.txt"

        # Check datetime parsing
        expected_dt = DateTime(2020, 3, 12, 5, 0, 0)
        @test obs.datetimes[1] == expected_dt

        # Check indices
        @test haskey(obs.by_station, "AAA")
        @test obs.by_station["AAA"] == [1]

        date_key = Date(2020, 3, 12)
        @test haskey(obs.by_date, date_key)
        @test obs.by_date[date_key] == [1]
    end

    @testset "Multiple observations and indices" begin
        obs = empty_observations()

        # Add multiple observations
        records = [
            ObservationRecord(rec="AAA", yyyymmdd=20200312, hhmmss=50000,
                            jdate=2458921.208333, avetime=0.041667,
                            conc=420.1, err=0.0451, num=1),
            ObservationRecord(rec="BBB", yyyymmdd=20200312, hhmmss=120000,
                            jdate=2458921.5, avetime=0.041667,
                            conc=418.5, err=0.0425, num=2),
            ObservationRecord(rec="AAA", yyyymmdd=20200313, hhmmss=50000,
                            jdate=2458922.208333, avetime=0.041667,
                            conc=421.2, err=0.0460, num=3)
        ]

        for rec in records
            add_observation!(obs, rec)
        end

        @test length(obs) == 3

        # Test station indexing
        aaa_indices = get_station_data(obs, "AAA")
        @test length(aaa_indices) == 2
        @test obs.stations[aaa_indices[1]] == "AAA"
        @test obs.stations[aaa_indices[2]] == "AAA"

        bbb_indices = get_station_data(obs, "BBB")
        @test length(bbb_indices) == 1
        @test obs.stations[bbb_indices[1]] == "BBB"

        # Test date indexing
        date1 = Date(2020, 3, 12)
        date1_indices = get_date_data(obs, date1)
        @test length(date1_indices) == 2

        date2 = Date(2020, 3, 13)
        date2_indices = get_date_data(obs, date2)
        @test length(date2_indices) == 1

        # Test get_stations
        stations = get_stations(obs)
        @test "AAA" in stations
        @test "BBB" in stations
        @test length(stations) == 2
    end

    @testset "Date filtering" begin
        obs = empty_observations()

        # Add observations across different dates
        records = [
            ObservationRecord(rec="AAA", yyyymmdd=20200310, hhmmss=50000,
                            jdate=2458919.208333, avetime=0.041667,
                            conc=419.0, err=0.0451, num=1),
            ObservationRecord(rec="AAA", yyyymmdd=20200312, hhmmss=50000,
                            jdate=2458921.208333, avetime=0.041667,
                            conc=420.1, err=0.0451, num=2),
            ObservationRecord(rec="AAA", yyyymmdd=20200315, hhmmss=50000,
                            jdate=2458924.208333, avetime=0.041667,
                            conc=421.5, err=0.0451, num=3)
        ]

        for rec in records
            add_observation!(obs, rec)
        end

        @test length(obs) == 3

        # Filter to keep only observations from 2020-03-12 to 2020-03-14
        filter_by_date_range!(obs, Date(2020, 3, 12), Date(2020, 3, 14))

        @test length(obs) == 1
        @test obs.concentrations[1] == 420.1
        @test Date(obs.datetimes[1]) == Date(2020, 3, 12)
    end

    @testset "Summary statistics" begin
        obs = empty_observations()

        # Test empty observations
        stats = summary_stats(obs)
        @test stats.count == 0

        # Add some test data
        records = [
            ObservationRecord(rec="AAA", yyyymmdd=20200312, hhmmss=50000,
                            jdate=2458921.208333, avetime=0.041667,
                            conc=420.0, err=0.04, num=1),
            ObservationRecord(rec="BBB", yyyymmdd=20200312, hhmmss=120000,
                            jdate=2458921.5, avetime=0.041667,
                            conc=430.0, err=0.05, num=2)
        ]

        for rec in records
            add_observation!(obs, rec)
        end

        stats = summary_stats(obs)
        @test stats.count == 2
        @test stats.n_stations == 2
        @test "AAA" in stats.stations
        @test "BBB" in stats.stations
        @test stats.concentration_range == (420.0, 430.0)
        @test stats.mean_concentration == 425.0
        @test stats.mean_error == 0.045
    end

    @testset "DateTime parsing" begin
        # Test various date/time combinations
        dt1 = parse_datetime(20200312, 50000)
        @test dt1 == DateTime(2020, 3, 12, 5, 0, 0)

        dt2 = parse_datetime(20201231, 235959)
        @test dt2 == DateTime(2020, 12, 31, 23, 59, 59)

        dt3 = parse_datetime(20200101, 0)
        @test dt3 == DateTime(2020, 1, 1, 0, 0, 0)
    end
end

@testset "Observations I/O" begin
    @testset "Header detection" begin
        # Test header line detection
        @test is_header_line("rec yyyymmdd hhmmss juldate avetime conc err num")
        @test is_header_line("rec station yyyymmdd hhmmss juldate avetime conc err")  # Alternative header with enough keywords
        @test !is_header_line("AAA 20200312 50000 2458921.208333 0.041667 420.1000 0.0451 1")
        @test !is_header_line("simple data line without keywords")
    end

    @testset "Observation line parsing" begin
        # Test standard format parsing
        line = "AAA 20200312 50000 2458921.208333 0.041667 420.1000 0.0451 1"
        record = parse_observation_line(line, 1, "test.txt")

        @test record !== nothing
        @test record.rec == "AAA"
        @test record.yyyymmdd == 20200312
        @test record.hhmmss == 50000
        @test record.jdate ≈ 2458921.208333
        @test record.avetime ≈ 0.041667
        @test record.conc ≈ 420.1000
        @test record.err ≈ 0.0451
        @test record.num == 1

        # Test line with missing data flag
        line_missing = "AAA 20200312 50000 2458921.208333 0.041667 -999.0 0.0451 1"
        record_missing = parse_observation_line(line_missing, 1, "test.txt")
        @test record_missing === nothing  # Should be filtered out

        # Test malformed line
        line_bad = "AAA 20200312 50000"  # Too few columns
        record_bad = parse_observation_line(line_bad, 1, "test.txt")
        @test record_bad === nothing
    end

    @testset "File reading with temporary files" begin
        # Create a temporary observation file
        temp_dir = mktempdir()
        temp_file = joinpath(temp_dir, "AAA_24.txt")

        # Write test data
        open(temp_file, "w") do io
            println(io, "rec yyyymmdd hhmmss juldate avetime conc err num")
            println(io, "AAA 20200312 50000 2458921.208333 0.041667 420.1000 0.0451 1")
            println(io, "AAA 20200312 120000 2458921.500000 0.041667 418.5000 0.0425 2")
            println(io, "AAA 20200313 50000 2458922.208333 0.041667 421.2000 0.0460 3")
        end

        # Test reading
        obs = read_observations(temp_dir, "_24"; use_obsfiles=false)

        @test length(obs) == 3
        @test length(obs.by_station) == 1
        @test haskey(obs.by_station, "AAA")
        @test length(obs.by_station["AAA"]) == 3

        # Check data integrity
        @test obs.concentrations[1] ≈ 420.1
        @test obs.concentrations[2] ≈ 418.5
        @test obs.concentrations[3] ≈ 421.2

        # Test date filtering
        obs_filtered = read_observations(temp_dir, "_24"; datei=20200312, datef=20200312, use_obsfiles=false)
        @test length(obs_filtered) == 2  # Only observations from 2020-03-12

        # Cleanup
        rm(temp_dir; recursive=true)
    end

    @testset "obsfiles.txt format" begin
        # Create temporary directory with obsfiles.txt
        temp_dir = mktempdir()

        # Create obsfiles.txt
        obsfiles_path = joinpath(temp_dir, "obsfiles.txt")
        open(obsfiles_path, "w") do io
            println(io, "1")
            println(io, "AAA_24.txt")
        end

        # Create the actual data file
        data_file = joinpath(temp_dir, "AAA_24.txt")
        open(data_file, "w") do io
            println(io, "rec yyyymmdd hhmmss juldate avetime conc err num")
            println(io, "AAA 20200312 50000 2458921.208333 0.041667 420.1000 0.0451 1")
        end

        # Test reading with obsfiles.txt
        obs = read_observations(temp_dir, "_24"; use_obsfiles=true)

        @test length(obs) == 1
        @test length(obs.obsfiles_list) == 1
        @test basename(obs.obsfiles_list[1]) == "AAA_24.txt"

        # Cleanup
        rm(temp_dir; recursive=true)
    end
end